﻿using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Xml.Linq;

namespace LinqDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //Student11 s = new Student11()
            //{
            //    Id = 1,
            //    Age = 2,
            //    Name = "111"
            //};

            //Stopwatch stopwatch = Stopwatch.StartNew();
            //for (int i = 0; i < 100000; i++)
            //{
            //    Student22 value1 = Mapping<Student11, Student22>.To(s);
            //}
            //stopwatch.Stop();
            //Console.WriteLine("调用: {0}ms", stopwatch.ElapsedMilliseconds);

            //Test2();

            ExpressionTest.Init();


            Console.WriteLine("Hello, World!");
        }

        private static void Test2()
        {
            People people = new People { Name = "Wayne" };
            Stopwatch stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 10000000; i++)
            {
                object value1 = people.Name;
            }
            stopwatch.Stop();
            Console.WriteLine("直接调用: {0}ms", stopwatch.ElapsedMilliseconds);

            Type type = typeof(People);
            PropertyInfo property = type.GetProperty("Name");
            stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 10000000; i++)
            {
                object value2 = property.GetValue(people);
            }
            stopwatch.Stop();
            Console.WriteLine("反射: {0}ms", stopwatch.ElapsedMilliseconds);

            var parameter = Expression.Parameter(type, "m");//参数m
            Expression expProperty = Expression.Property(parameter, property.Name);//取参数的属性m.Name
            var propertyDelegateExpression = Expression.Lambda(expProperty, parameter);//变成表达式 m => m.Name
            var propertyDelegate = (Func<People, object>)propertyDelegateExpression.Compile();//编译成委托
            stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 10000000; i++)
            {
                object value3 = propertyDelegate.Invoke(people);
            }
            stopwatch.Stop();
            Console.WriteLine("Lambda:{0}ms", stopwatch.ElapsedMilliseconds);


            MemberGetDelegate memberGet = (MemberGetDelegate)System.Delegate.CreateDelegate(typeof(MemberGetDelegate), property.GetGetMethod());
            stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 10000000; i++)
            {
                object value4 = memberGet(people);
            }
            stopwatch.Stop();
            Console.WriteLine("Delegate: {0}ms", stopwatch.ElapsedMilliseconds);

            PropertyValue<People> propertyValue = new PropertyValue<People>(people);
            object value = propertyValue.Get("Name");
        }

        delegate object MemberGetDelegate(People p);

        private static void Test1()
        {
            //ParameterExpression param = Expression.Parameter(typeof(Student), "s");

            //ConstantExpression con1 = Expression.Constant(1, typeof(int));
            //MemberExpression state = Expression.Property(param, "State");
            //BinaryExpression aa = Expression.Equal(state, con1);

            //ConstantExpression con2 = Expression.Constant(2, typeof(int));
            //MemberExpression accout = Expression.Property(param, "Accout");
            //MemberExpression length = Expression.Property(accout, typeof(string).GetProperty("Length"));
            //BinaryExpression bb = Expression.GreaterThan(length, con2);

            //ConstantExpression con3 = Expression.Constant("12", typeof(string));
            //MemberExpression name = Expression.Property(param, "Name");
            //var method = typeof(string).GetMethod("StartsWith", [typeof(string)]);
            //MethodCallExpression start = Expression.Call(name, method, con3);

            //ConstantExpression con4 = Expression.Constant(100, typeof(int));
            //MemberExpression id = Expression.Property(param, "Id");
            //BinaryExpression cc = Expression.GreaterThan(id, con4);

            //BinaryExpression b1 = Expression.AndAlso(cc, start);
            //BinaryExpression b2 = Expression.AndAlso(b1, bb);
            //BinaryExpression b3 = Expression.AndAlso(b2, aa);

            //Expression<Func<Student, bool>> dd = Expression.Lambda<Func<Student, bool>>(b3, param);

            //Expression<Func<int, int>> ddd = s => s * 3 + 2;

            //Expression<Func<Student, bool>> hhh = s => s.Id > 100 && s.Name.StartsWith("12")
            //&& s.Accout.Length > 2 && s.State == 1;

            Student student = new Student()
            {
                State = 2,
                Accout = "124",
                Id = 3,
                Name = "345"
            };

            Expression<Func<Student, bool>> eee = s => s.Id == student.Id;

            ParameterExpression param = Expression.Parameter(typeof(Student), "t");
            var body = Expression.Property(param, "Id");

            ConstantExpression con = Expression.Constant(student);
            var body1 = Expression.Property(con, "Id");

            var abc = Expression.Equal(body, body1);

            var lambda = Expression.Lambda<Func<Student, bool>>(abc, param);



            //var aaa = ToExpression<Student,StudentDto>(student);

            //Expression<Func<Student, StudentDto>> lambda2 = s => new StudentDto
            //{
            //    State = 8,
            //    Id = 7,
            //    Accout = "46",
            //    Name = "hh"
            //};
            var aaa = student.GetPropertyValue("Name");

            student.SetPropertyValue("Name", "234");
        }

        private static TOut Trans<TIn, TOut>(TIn tIn)
        {
            TOut tOut = Activator.CreateInstance<TOut>();
            foreach (var item in tOut.GetType().GetProperties())
            {
                var prop = tIn.GetType().GetProperty(item.Name);
                item.SetValue(tOut, prop.GetValue(tIn));
            }
            return tOut;
        }

        private static TOut ToExpression<TIn, TOut>(TIn tIn)
        {
            ParameterExpression param = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> members = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())
            {
                MemberExpression prop = Expression.Property(param, item.Name);
                MemberBinding member = Expression.Bind(item, prop);
                members.Add(member);
            }

            MemberInitExpression init = Expression.MemberInit(Expression.New(typeof(TOut)), members.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(init, param);

            return lambda.Compile()(tIn);
        }
    }

    public class PropertyValue<T>
    {
        private static ConcurrentDictionary<string, MemberGetDelegate> _memberGetDelegate = new ConcurrentDictionary<string, MemberGetDelegate>();
        delegate object MemberGetDelegate(T obj);
        public PropertyValue(T obj)
        {
            Target = obj;
        }
        public T Target { get; private set; }
        public object Get(string name)
        {
            MemberGetDelegate memberGet = _memberGetDelegate.GetOrAdd(name, BuildDelegate);
            return memberGet(Target);
        }
        private MemberGetDelegate BuildDelegate(string name)
        {
            Type type = typeof(T);
            PropertyInfo property = type.GetProperty(name);
            return (MemberGetDelegate)Delegate.CreateDelegate(typeof(MemberGetDelegate), property.GetGetMethod());
        }
    }

    public static class ObjectExtension
    {
        public static object GetPropertyValue<T>(this T t, string name)
        {
            ParameterExpression param = Expression.Parameter(typeof(T), "t");
            var body = Expression.Property(param, name);
            var lambda = Expression.Lambda<Func<T, object>>(body, param);
            var value = lambda.Compile()(t);
            return value;
        }

        public static void SetPropertyValue<T>(this T t, string name, object value)
        {
            var e = Expression.Parameter(typeof(T), "t");
            var property = Expression.Property(e, name);
            var assignString = Expression.Assign(property, Expression.Constant(value));
            Expression.Lambda<Action<T>>(assignString, e).Compile()(t);
        }
    }

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Accout { get; set; }
        public int State { get; set; }
    }
    public class StudentDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Accout { get; set; }
        public int State { get; set; }
    }

    public class Student11
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class Student22
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
